//
// Created by AssAs on 2024/9/23.
//

#include "audio_wrapper.h"

#include "audio_element.h"
#include <audio_mem.h>
#include <audio_pipeline.h>
#include <audio_recorder.h>
#include <audio_thread.h>
#include <audio_tone_uri.h>
#include <board.h>
#include <cJSON.h>
#include <esp_audio.h>
#include <esp_http_client.h>
#include <esp_log.h>
#include <esp_tls.h>
#include <filter_resample.h>
#include <http_stream.h>
#include <i2s_stream.h>
#include <lightduer_dcs.h>
#include <mp3_decoder.h>
#include <raw_stream.h>
#include <recorder_encoder.h>
#include <recorder_sr.h>
#include <spiffs_stream.h>
#include <stddef.h>
#include <wav_encoder.h>
#include <freertos/semphr.h>
#include <esp_heap_caps.h>
#include <my_led_strip.h>
#include <tone_stream.h>
#include <wav_decoder.h>

/*---------------------------parameters------------------------------*/
static const char *TAG = "AUDIO_WRAPPER";
static SemaphoreHandle_t            s_mutex     = NULL;
static esp_audio_handle_t           player      = NULL;
static audio_rec_handle_t           recorder    = NULL;
static audio_element_handle_t       raw_read    = NULL;
static int player_pause = 0;
static int audio_pos = 0;
static int duer_playing_type    = DUER_AUDIO_TYPE_UNKOWN;
static int chat_playing_type    = PLAY_STOP;
QueueHandle_t          rec_q         = NULL;
bool voice_reading = false;
extern EventGroupHandle_t play_event_group;

static audio_element_handle_t http_stream_reader, i2s_writer_h, mp3_decoder;
static audio_event_iface_handle_t evt;
static QueueHandle_t url_queue;
#define MAX_URL_LENGTH 2048
#define MAX_URL_COUNT 20

typedef struct {
    char **urls;  // 使用指针数组
    int count;
} url_list_t;

static url_list_t url_list = {0};


#define BOUNDARY "--------------------------ESP32Boundary"
#define SERVER_URL "" //服务器地址
#define HTTP_BUF_SIZE 1024 * 4
#define MAX_AUDIO_SIZE (1024 * 1024)  // 10MB max, adjust as needed

static uint8_t *psram_buffer = NULL;
static size_t psram_buffer_size = 0;
static size_t current_position = 0;

bool is_send_done = false;
int current_volume = 60; // 当前音量

bool is_audio_error = false;
extern bool button_recorder;
/*------------------------------------------------------------------*/

/*-----------------------main---------------------*/
extern char BT_MAC[35];
extern int msgId;
/*------------------------------------------------*/

/*-----------------------sysConfig---------------------*/
extern char *SERVER_CHAT_URL;
/*------------------------------------------------*/

/*-----------------------LED---------------------*/
extern int led_state;
/*------------------------------------------------*/

int _http_stream_event_handle(http_stream_event_msg_t *msg)
{
    // if (msg->event_id == HTTP_STREAM_RESOLVE_ALL_TRACKS) {
    //     return ESP_OK;
    // }
    //
    // if (msg->event_id == HTTP_STREAM_FINISH_TRACK) {
    //     return http_stream_next_track(msg->el);
    // }
    // if (msg->event_id == HTTP_STREAM_FINISH_PLAYLIST) {
    //     return http_stream_restart(msg->el);
    // }
    return ESP_OK;
}

void url_list_init()
{
    // 从PSRAM分配内存
    url_list.urls = (char **)heap_caps_malloc(MAX_URL_COUNT * sizeof(char *), MALLOC_CAP_SPIRAM);
    if (url_list.urls == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for url_list.urls");
        return;
    }

    // 为每个URL分配内存
    for (int i = 0; i < MAX_URL_COUNT; i++) {
        url_list.urls[i] = (char *)heap_caps_malloc(MAX_URL_LENGTH * sizeof(char), MALLOC_CAP_SPIRAM);
        if (url_list.urls[i] == NULL) {
            ESP_LOGE(TAG, "Failed to allocate memory for url_list.urls[%d]", i);
            return;
        }
    }

    url_list.count = 0; // 初始化
    ESP_LOGI(TAG, "url_list initialized successfully");
}

void url_list_deinit()
{
    // 释放每个URL的内存
    for (int i = 0; i < MAX_URL_COUNT; i++) {
        if (url_list.urls[i]) {
            free(url_list.urls[i]);
        }
    }

    // 释放url列表的指针
    if (url_list.urls) {
        free(url_list.urls);
    }
}

static void clear_url_list(void)
{
    // memset(&url_list, 0, sizeof(url_list_t));
    // 清除每个URL的内容，但不释放内存
    for (int i = 0; i < url_list.count; i++) {
        memset(url_list.urls[i], 0, MAX_URL_LENGTH); // 将每个URL存储空间清零
    }
    url_list.count = 0;  // 重置计数器
    ESP_LOGI(TAG,"qingchuqingchu-----------------");
}

static void url_list_task(void *pvParameters)
{
    while (1) {
        // 检查是否有URL在列表中，且当前没有在播放
        if(is_audio_error) {
            is_audio_error = false;
            xSemaphoreTake(s_mutex, portMAX_DELAY);
            chat_playing_type = PLAY_STOP;
            xSemaphoreGive(s_mutex);
        }
        if (url_list.count > 0 && !(chat_playing_type == PLAY_PLAYING) && !is_send_done) {
            // 发送第一个URL到队列
            if (xQueueSend(url_queue, url_list.urls[0], pdMS_TO_TICKS(1000)) == pdTRUE) {
                ESP_LOGI(TAG, "Sent URL to queue: %s", url_list.urls[0]);
                ESP_LOGE(TAG, "chat_playing_type %d", chat_playing_type);
                is_send_done = true;
                // 移除已发送的URL
                for (int i = 0; i < url_list.count - 1; i++) {
                    strcpy(url_list.urls[i], url_list.urls[i + 1]);
                }
                url_list.count--;
            } else {
                ESP_LOGE(TAG, "Failed to send URL to queue");
            }
        }
        // 短暂延迟以避免过度消耗CPU
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

static void esp_audio_state_task (void *para)
{
    QueueHandle_t que = (QueueHandle_t) para;
    esp_audio_state_t esp_state = {0};
    while (1) {
        xQueueReceive(que, &esp_state, portMAX_DELAY);
        ESP_LOGE(TAG, "esp_audio status:%x,err:%x,state:%d", esp_state.status, esp_state.err_msg, duer_playing_type);
        switch (esp_state.status) {
            case AUDIO_STATUS_RUNNING:
                ESP_LOGE(TAG, "Audio is now playing");
                xSemaphoreTake(s_mutex, portMAX_DELAY);
                chat_playing_type = PLAY_PLAYING;
                xSemaphoreGive(s_mutex);
                break;
            case AUDIO_STATUS_STOPPED:
                ESP_LOGE(TAG, "Audio playback ended");
                is_send_done = false;
                xSemaphoreTake(s_mutex, portMAX_DELAY);
                chat_playing_type = PLAY_STOP;
                xSemaphoreGive(s_mutex);
                clear_url_list();
            break;
            case AUDIO_STATUS_FINISHED:
                ESP_LOGE(TAG, "Audio playback finished");
                is_send_done = false;
                xSemaphoreTake(s_mutex, portMAX_DELAY);
                chat_playing_type = PLAY_STOP;
                xSemaphoreGive(s_mutex);
            break;
            case AUDIO_STATUS_ERROR:
                ESP_LOGE(TAG, "Audio playback error");
                is_send_done = false;
                is_audio_error = true;
                xSemaphoreTake(s_mutex, portMAX_DELAY);
                chat_playing_type = PLAY_STOP;
                xSemaphoreGive(s_mutex);
                ESP_LOGI(TAG, "chat_playing_type: %d, is_send_done: %d", chat_playing_type, is_send_done);
                vTaskDelay(100);
            break;
            case AUDIO_STATUS_PAUSED:
                ESP_LOGE(TAG, "Audio playback paused");
            break;
            case AUDIO_STATUS_UNKNOWN:
                ESP_LOGE(TAG, "Audio playback unknow");
            break;
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
    vTaskDelete(NULL);
}
static void mp3_playback_task(void *pvParameters) {
    char *url = (char *)heap_caps_malloc(MAX_URL_LENGTH * sizeof(char), MALLOC_CAP_SPIRAM);

    while (1) {
        if (xQueueReceive(url_queue, url, portMAX_DELAY) == pdTRUE) {
            ESP_LOGI(TAG, "[ * ] Received URL from queue: %s", url);
            chat_speak_handler(url);
            xSemaphoreTake(s_mutex, portMAX_DELAY);
            chat_playing_type = PLAY_PLAYING;
            xSemaphoreGive(s_mutex);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
    free(url);
    vTaskDelete(NULL);
}

void chat_audio_player_play(const char *url)
{
    ESP_LOGI(TAG, "audio_player_play URL: %s", url);

    if (strlen(url) >= MAX_URL_LENGTH) {
        ESP_LOGE(TAG, "URL is too long: %d", strlen(url));
        return;
    }

    ESP_LOGI(TAG,"<<<<<<url_list.count： %d>>>>>>>",url_list.count);
    if (url_list.count < MAX_URL_COUNT) {
        strcpy(url_list.urls[url_list.count], url);
        url_list.count++;
        //打印chat_playing_type 和 is_send_done
        ESP_LOGI(TAG, "chat_playing_type: %d, is_send_done: %d", chat_playing_type, is_send_done);
        ESP_LOGI(TAG, "Added URL to list. Count: %d", url_list.count);
    } else {
        ESP_LOGE(TAG, "URL list is full");
    }
}


void *chat_audio_setup_player(void)
{
    if (player) {
        return player;
    }
    esp_audio_cfg_t cfg = DEFAULT_ESP_AUDIO_CONFIG();
    audio_board_handle_t board_handle = audio_board_init();
    cfg.vol_handle = board_handle->audio_hal;
    cfg.vol_set = (audio_volume_set)audio_hal_set_volume;
    cfg.vol_get = (audio_volume_get)audio_hal_get_volume;
    cfg.resample_rate = 48000;
    cfg.prefer_type = ESP_AUDIO_PREFER_SPEED;
    cfg.evt_que = xQueueCreate(3, sizeof(esp_audio_state_t));

    player = esp_audio_create(&cfg);

    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);
    bool ext_stack = false;
    if (audio_mem_spiram_stack_is_enabled()) {
        ext_stack = true;
    }
    audio_thread_create(NULL, "player_task", esp_audio_state_task, cfg.evt_que, 3 * 1024, 8, ext_stack, 1);

    spiffs_stream_cfg_t spiffs_cfg = SPIFFS_STREAM_CFG_DEFAULT();
    spiffs_cfg.type = AUDIO_STREAM_READER;
    esp_audio_input_stream_add(player, spiffs_stream_init(&spiffs_cfg));


    http_stream_cfg_t http_cfg = HTTP_STREAM_CFG_DEFAULT();
    http_cfg.event_handle = _http_stream_event_handle;
    http_cfg.type = AUDIO_STREAM_READER;
    http_cfg.task_prio = 9;
    // http_cfg.task_stack = 8 * 1024;
    http_cfg.enable_playlist_parser = true;
    http_stream_reader = http_stream_init(&http_cfg);
    esp_audio_input_stream_add(player, http_stream_reader);

    // Create writers and add to esp_audio
    i2s_stream_cfg_t i2s_writer = I2S_STREAM_CFG_DEFAULT_WITH_PARA(0, 48000, 32, AUDIO_STREAM_WRITER);
    // i2s_writer.need_expand = (CODEC_ADC_BITS_PER_SAMPLE != 16);
    i2s_writer.need_expand = true;
    i2s_writer.type = AUDIO_STREAM_WRITER;
    i2s_writer_h = i2s_stream_init(&i2s_writer);

    // Add decoders and encoders to esp_audio
    mp3_decoder_cfg_t mp3_dec_cfg = DEFAULT_MP3_DECODER_CONFIG();
    mp3_dec_cfg.task_core = 1;
    mp3_decoder = mp3_decoder_init(&mp3_dec_cfg);
    esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, mp3_decoder);

    wav_decoder_cfg_t wav_dec_cfg = DEFAULT_WAV_DECODER_CONFIG();
    audio_element_handle_t wav_decoder = wav_decoder_init(&wav_dec_cfg);
    esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, wav_decoder);

    esp_audio_output_stream_add(player, i2s_writer_h);

    // Set default volume
    esp_audio_vol_set(player, current_volume);
    AUDIO_MEM_SHOW(TAG);
    ESP_LOGI(TAG, "esp_audio instance is:%p", player);
    ESP_LOGI(TAG, "Audio player setup complete");

    url_list_init();
    url_queue = xQueueCreate(MAX_URL_COUNT, MAX_URL_LENGTH);
    xTaskCreate(url_list_task, "url_list_task", 1024 * 4, NULL, 5, NULL);  // 创建新任务
    xTaskCreate(mp3_playback_task, "mp3_playback_task", 1024 * 4, NULL, 5, NULL);

    return player;
}

static int input_cb_for_afe(int16_t *buffer, int buf_sz, void *user_ctx, TickType_t ticks)
{
    return raw_stream_read(raw_read, (char *)buffer, buf_sz);
}

esp_err_t _http_event_handle(esp_http_client_event_t *evt)
{
    static char *output_buffer; // Buffer to store response of http request from event handler
    static int output_len;      // Stores number of bytes read
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            printf("%.*s", evt->data_len, (char*)evt->data);
            if (!esp_http_client_is_chunked_response(evt->client))
            {
                // If user_data buffer is configured, copy the response into the buffer
                if (evt->user_data)
                {
                    memcpy(evt->user_data + output_len, evt->data, evt->data_len);
                }
                else
                {
                    if (output_buffer == NULL)
                    {
                        output_buffer = (char *)malloc(esp_http_client_get_content_length(evt->client));
                        output_len = 0;
                        if (output_buffer == NULL)
                        {
                            ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
                            return ESP_FAIL;
                        }
                    }
                    memcpy(output_buffer + output_len, evt->data, evt->data_len);
                    output_len += evt->data_len;
                }
            }

            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
            if (output_buffer != NULL)
            {
                // 在此处处理完整接收到的 HTTP 响应
                ESP_LOGI(TAG, "Full response: %s", output_buffer);
                cJSON *root = cJSON_Parse(output_buffer);
                if (root) {
                    // play_tone(PING);
                    led_state = LED_OFF;
                    if (cJSON_GetObjectItem(root, "bot_voice_url") != NULL && cJSON_GetObjectItem(root, "bot_voice_url")->valuestring != NULL && strlen(cJSON_GetObjectItem(root, "bot_voice_url")->valuestring) > 0) {
                        // 如果是以wav结尾的就跳过
                         if (strstr(cJSON_GetObjectItem(root, "bot_voice_url")->valuestring, "wav") == NULL) {
                            ESP_LOGI(TAG, "Received bot voice URL: %s", cJSON_GetObjectItem(root, "bot_voice_url")->valuestring);
                            chat_audio_player_play(cJSON_GetObjectItem(root, "bot_voice_url")->valuestring);
                        }
                    }
                    cJSON_Delete(root);
                }
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            int mbedtls_err = 0;
            esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
            if (err != 0) {
                ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
                ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
            }
            break;
        case HTTP_EVENT_REDIRECT:
            ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT");
            break;
    }
    return ESP_OK;
}
static esp_err_t send_audio_with_params(const char *url, const uint8_t *audio_data, size_t audio_size, const char *device_id)
{
    esp_http_client_config_t config = {
            .url = url,
            .event_handler = _http_event_handle,
            .buffer_size = HTTP_BUF_SIZE / 6,
            .buffer_size_tx = HTTP_BUF_SIZE,
            .timeout_ms = 5000,
            .method = HTTP_METHOD_POST,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_http_client_set_header(client, "Content-Type", "multipart/form-data; boundary=--------------------------ESP32Boundary");

    // Construct multipart form data
    char form_data_start[1000];
    snprintf(form_data_start, sizeof(form_data_start),
             "--%s\r\n"
             "Content-Disposition: form-data; name=\"devId\"\r\n\r\n%s\r\n"
             "--%s\r\n"
             "Content-Disposition: form-data; name=\"msgId\"\r\n\r\n%d\r\n"
             "--%s\r\n"
             "Content-Disposition: form-data; name=\"image_id\"\r\n\r\n%s\r\n"
             "--%s\r\n"
             "Content-Disposition: form-data; name=\"respond_format\"\r\n\r\nmp3\r\n"
             "--%s\r\n"
             "Content-Disposition: form-data; name=\"audio_file\"; filename=\"recorded_audio.wav\"\r\n"
             "Content-Type: audio/wave\r\n\r\n",
             BOUNDARY, device_id, BOUNDARY, msgId, BOUNDARY, "0", BOUNDARY, BOUNDARY);

    char form_data_end[128];
    snprintf(form_data_end, sizeof(form_data_end), "\r\n--%s--", BOUNDARY);

    // Calculate total size
    int total_size = strlen(form_data_start) + audio_size + strlen(form_data_end);

    // Allocate memory for the entire POST data
    char *post_data = heap_caps_malloc(total_size, MALLOC_CAP_SPIRAM);
    if (!post_data) {
        ESP_LOGE(TAG, "Failed to allocate memory for POST data");
        esp_http_client_cleanup(client);
        return ESP_FAIL;
    }

    // Fill the POST data
    memcpy(post_data, form_data_start, strlen(form_data_start));
    memcpy(post_data + strlen(form_data_start), audio_data, audio_size);
    memcpy(post_data + strlen(form_data_start) + audio_size, form_data_end, strlen(form_data_end));

    //打印post_data 和 audio_size
    // ESP_LOGI(TAG, "post_data: %s", post_data);



    // Set POST data
    esp_err_t err = esp_http_client_set_post_field(client, post_data, total_size);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to set POST field: %s", esp_err_to_name(err));
        heap_caps_free(post_data);
        esp_http_client_cleanup(client);
        return err;
    }
    ESP_LOGE(TAG, "http post start<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
    // Perform HTTP POST request
    err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        int status_code = esp_http_client_get_status_code(client);
        int content_length = esp_http_client_get_content_length(client);
        ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d", status_code, content_length);
    } else {
        ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
    }

    // Clean up
    heap_caps_free(post_data);
    esp_http_client_cleanup(client);

    return err;
}

static void init_psram_buffer()
{
    if (psram_buffer == NULL) {
        psram_buffer = heap_caps_malloc(MAX_AUDIO_SIZE, MALLOC_CAP_SPIRAM);
        if (psram_buffer == NULL) {
            ESP_LOGE(TAG, "Failed to allocate PSRAM buffer");
        } else {
            psram_buffer_size = MAX_AUDIO_SIZE;
            current_position = 0;
            ESP_LOGI(TAG, "PSRAM buffer allocated: %d bytes", psram_buffer_size);
        }
    }
}

// Helper function to write WAV header
static void write_wav_header(uint8_t *header, size_t data_size, int sample_rate, int bits_per_sample, int channels)
{
    memcpy(header, "RIFF", 4);
    *(uint32_t *)(header + 4) = data_size + 36;
    memcpy(header + 8, "WAVE", 4);
    memcpy(header + 12, "fmt ", 4);
    *(uint32_t *)(header + 16) = 16;
    *(uint16_t *)(header + 20) = 1;
    *(uint16_t *)(header + 22) = channels;
    *(uint32_t *)(header + 24) = sample_rate;
    *(uint32_t *)(header + 28) = sample_rate * channels * bits_per_sample / 8;
    *(uint16_t *)(header + 32) = channels * bits_per_sample / 8;
    *(uint16_t *)(header + 34) = bits_per_sample;
    memcpy(header + 36, "data", 4);
    *(uint32_t *)(header + 40) = data_size;
}

void voice_2_psram(uint8_t *buffer, int len)
{
    if (psram_buffer == NULL) {
        init_psram_buffer();
        if (psram_buffer == NULL) return;
    }

    if (voice_reading) {
        if (current_position + len <= psram_buffer_size) {
            memcpy(psram_buffer + current_position, buffer, len);
            current_position += len;
        } else {
            ESP_LOGW(TAG, "PSRAM buffer full, data truncated");
        }
    }
}

static void clear_psram_buffer()
{
    current_position = 0;  // Reset the buffer position for next recording
    ESP_LOGI(TAG, "PSRAM buffer cleared");
}

static void upload_and_clear_recording()
{
    if (current_position > 0) {
        size_t wav_data_size = current_position;
        size_t wav_total_size = wav_data_size + 44;  // WAV header size is 44 bytes

        // Create a new buffer to hold the WAV header + audio data
        uint8_t *wav_buffer = heap_caps_malloc(wav_total_size, MALLOC_CAP_SPIRAM);
        if (!wav_buffer) {
            ESP_LOGE(TAG, "Failed to allocate memory for WAV buffer");
            return;
        }

        // Write WAV header to the buffer
        write_wav_header(wav_buffer, wav_data_size, 16000, 16, 1);

        // Append audio data after the WAV header
        memcpy(wav_buffer + 44, psram_buffer, wav_data_size);

        // Upload the WAV file
        send_audio_with_params(SERVER_CHAT_URL, wav_buffer, wav_total_size, BT_MAC);

        // Free the WAV buffer after upload
        heap_caps_free(wav_buffer);

        // Clear PSRAM buffer for next recording
        clear_psram_buffer();
    }
}

static void voice_read_task(void *args)
{
    const int buf_len = 2 * 1024;
    uint8_t *voiceData = audio_calloc(1, buf_len);
    int msg = 0;
    TickType_t delay = portMAX_DELAY;

    while (true) {
        if (xQueueReceive(rec_q, &msg, delay) == pdTRUE) {
            switch (msg) {
                case REC_START: {
                    ESP_LOGW(TAG, "voice read begin");
                    delay = 0;
                    voice_reading = true;
                    current_position = 0;  // Start a new recording
                    break;
                }
                case REC_STOP: {
                    ESP_LOGW(TAG, "voice read stopped");
                    delay = portMAX_DELAY;
                    voice_reading = false;
                    // Upload and clear the recording
                    upload_and_clear_recording();
                    break;
                }
                case REC_CANCEL: {
                    ESP_LOGW(TAG, "voice read cancel");
                    delay = portMAX_DELAY;
                    voice_reading = false;
                    clear_psram_buffer();  // Discard this recording
                    break;
                }
                default:
                    break;
            }
        }
        int ret = 0;
        if (voice_reading) {
            ret = audio_recorder_data_read(recorder, voiceData, buf_len, portMAX_DELAY);
            if (ret <= 0) {
                if (button_recorder) {
                    ESP_LOGW(TAG, "button_recorder read continue %d", ret);
                }else {
                    ESP_LOGW(TAG, "audio recorder read finished %d", ret);
                    delay = portMAX_DELAY;
                    voice_reading = false;
                }
            }
        }
        voice_2_psram(voiceData, ret);
        vTaskDelay(10);
    }
    free(voiceData);
    vTaskDelete(NULL);
}

void *chat_audio_start_recorder(rec_event_cb_t cb)
{
    if (recorder) {
        return recorder;
    }

    audio_element_handle_t i2s_stream_reader;
    audio_pipeline_handle_t pipeline;
    audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    pipeline = audio_pipeline_init(&pipeline_cfg);
    if (NULL == pipeline) {
        return NULL;
    }

    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(CODEC_ADC_I2S_PORT, 48000, 32, AUDIO_STREAM_READER);
    i2s_cfg.task_core = 1;
    i2s_stream_reader = i2s_stream_init(&i2s_cfg);

    audio_element_handle_t filter = NULL;
    rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
    rsp_cfg.src_rate = 48000;
    rsp_cfg.dest_rate = 16000;
    rsp_cfg.task_core = 1;
    filter = rsp_filter_init(&rsp_cfg);

    raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
    raw_cfg.type = AUDIO_STREAM_READER;
    raw_read = raw_stream_init(&raw_cfg);

    audio_pipeline_register(pipeline, i2s_stream_reader, "i2s");
    audio_pipeline_register(pipeline, raw_read, "raw");

    if (filter) {
        audio_pipeline_register(pipeline, filter, "filter");
        const char *link_tag[3] = {"i2s", "filter", "raw"};
        audio_pipeline_link(pipeline, &link_tag[0], 3);
    } else {
        const char *link_tag[2] = {"i2s", "raw"};
        audio_pipeline_link(pipeline, &link_tag[0], 2);
    }

    audio_pipeline_run(pipeline);
    ESP_LOGI(TAG, "Recorder has been created");

    recorder_sr_cfg_t recorder_sr_cfg = DEFAULT_RECORDER_SR_CFG();
    recorder_sr_cfg.afe_cfg.aec_init = RECORD_HARDWARE_AEC;
    recorder_sr_cfg.multinet_init = false;
    recorder_sr_cfg.afe_cfg.vad_mode = VAD_MODE_4;
    recorder_sr_cfg.afe_cfg.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
    recorder_sr_cfg.afe_cfg.agc_mode = AFE_MN_PEAK_NO_AGC;

    recorder_encoder_cfg_t recorder_encoder_cfg = { 0 };
    wav_encoder_cfg_t wav_cfg = DEFAULT_WAV_ENCODER_CONFIG();
    recorder_encoder_cfg.encoder = wav_encoder_init(&wav_cfg);

    audio_rec_cfg_t cfg = AUDIO_RECORDER_DEFAULT_CFG();
    cfg.read = (recorder_data_read_t)&input_cb_for_afe;
    cfg.sr_handle = recorder_sr_create(&recorder_sr_cfg, &cfg.sr_iface);
    // cfg.encoder_handle = recorder_encoder_create(&recorder_encoder_cfg, &cfg.encoder_iface);
    cfg.event_cb = cb;
    cfg.vad_off = 1000;
    recorder = audio_recorder_create(&cfg);

    rec_q = xQueueCreate(20, sizeof(int));
    audio_thread_create(NULL, "read_task", voice_read_task, NULL, 4 * 1024, 8, true, 0);

    init_psram_buffer();  // Initialize PSRAM buffer

    return recorder;
}



void audio_wrapper_init(void)
{
    if (s_mutex == NULL) {
        s_mutex = xSemaphoreCreateMutex();
        if (s_mutex == NULL) {
            ESP_LOGE(TAG, "Failed to create mutex");
        } else {
            ESP_LOGD(TAG, "Create mutex success");
        }
    }
}

void chat_audio_wrapper_pause()
{
    esp_audio_stop(player, 0);
    ESP_LOGW(TAG, "duer_audio_wrapper_pause, playing type:%d", duer_playing_type);
}

int chat_audio_wrapper_get_state()
{
    esp_audio_state_t st = {0};
    esp_audio_state_get(player, &st);
    return st.status;
}


void chat_listen_handler(void)
{
    ESP_LOGI(TAG, "enable_listen_handler, open mic");
    audio_recorder_trigger_start(recorder);
}


void chat_stop_listen_handler(void)
{
    ESP_LOGI(TAG, "stop_listen, close mic");
    audio_recorder_trigger_stop(recorder);
}

void chat_volume_set_handler(int volume)
{
    ESP_LOGI(TAG, "set_volume to %d", volume);
    int ret = esp_audio_vol_set(player, volume);
    if (ret == 0) {
        ESP_LOGI(TAG, "report on_volume_changed");
    }
}

void chat_volume_adjust_handler(int volume)
{
    ESP_LOGI(TAG, "adj_volume by %d", volume);
    int vol = 0;
    esp_audio_vol_get(player, &vol);
    vol += volume;
    int ret = esp_audio_vol_set(player, vol);
    if (ret == 0) {
        ESP_LOGI(TAG, "report on_volume_changed");

    }
}

void chat_mute_handler(bool is_mute)
{
    ESP_LOGI(TAG, "set_mute to  %d", (int)is_mute);
    int ret = 0;
    if (is_mute) {
        ret = esp_audio_vol_set(player, 0);
    }
    if (ret == 0) {
        ESP_LOGI(TAG, "report on_mute");
        // duer_dcs_on_mute();//通知 DuerOS 静音状态
    }
}
/*
 *获取扬声器的当前状态（音量和静音状态）。
 *
 */
void chat_get_speaker_state(int *volume, bool *is_mute)
{
    ESP_LOGI(TAG, "duer_dcs_get_speaker_state");
    *volume = 60;
    *is_mute = false;
    int ret = 0;
    int vol = 0;
    ret = esp_audio_vol_get(player, &vol);
    if (ret != 0) {
        ESP_LOGE(TAG, "Failed to get volume");
    } else {
        *volume = vol;
        if (vol != 0) {
            *is_mute = false;
        } else {
            *is_mute = true;
        }
    }
}

/*
 * 播放指定的语音文件。
 */
void chat_speak_handler(const char *url)
{
    ESP_LOGI(TAG, "Playing speak: %s", url);
    esp_audio_play(player, AUDIO_CODEC_TYPE_DECODER, url, 0);
    chat_playing_type = PLAY_PLAYING;
}

void chat_audio_play_handler(const chat_audio_info_t *audio_info)
{
    ESP_LOGI(TAG, "Playing audio, offset:%d url:%s", audio_info->offset, audio_info->url);
    esp_audio_play(player, AUDIO_CODEC_TYPE_DECODER, audio_info->url, audio_info->offset);
    xSemaphoreTake(s_mutex, portMAX_DELAY);
    duer_playing_type = DUER_AUDIO_TYPE_MUSIC;
    player_pause = 0;
    xSemaphoreGive(s_mutex);
}

void chat_audio_stop_handler()
{
    ESP_LOGI(TAG, "Stop audio play");
    xSemaphoreTake(s_mutex, portMAX_DELAY);
    int status = duer_playing_type;
    xSemaphoreGive(s_mutex);
    if (status == 1) {
        ESP_LOGI(TAG, "Is playing speech, no need to stop");
    } else {
        esp_audio_stop(player, TERMINATION_TYPE_NOW);
    }
}

void chat_audio_stop_NOW() {
    ESP_LOGI(TAG, "Stop audio play");
    esp_audio_stop(player, TERMINATION_TYPE_NOW);
}

void chat_audio_player_stop() {
    esp_audio_stop(player, TERMINATION_TYPE_NOW);
}

void chat_audio_pause_handler()
{
    ESP_LOGI(TAG, "DCS pause audio play");
    esp_audio_pos_get(player, &audio_pos);
    player_pause = 1;
    esp_audio_stop(player, 0);
}
void chat_audio_resume_handler(const chat_audio_info_t *audio_info)
{
    ESP_LOGI(TAG, "Resume audio, offset:%d url:%s", audio_info->offset, audio_info->url);
    player_pause = 0;
    duer_playing_type = DUER_AUDIO_TYPE_MUSIC;
    esp_audio_play(player, AUDIO_CODEC_TYPE_DECODER, audio_info->url, audio_pos);
}

int chat_audio_get_play_progress()
{
    int ret = 0;
    uint32_t total_size = 0;
    int position = 0;
    ret = esp_audio_pos_get(player, &position);
    if (ret == 0) {
        ESP_LOGI(TAG, "Get play position %d of %" PRIu32, position, total_size);
        return position;
    } else {
        ESP_LOGE(TAG, "Failed to get play progress.");
        return -1;
    }
}
duer_audio_type_t chat_get_player_type()
{
    return duer_playing_type;
}

bool chat_audio_player_is_playing(void)
{
    return chat_playing_type == PLAY_PLAYING;
}


int chat_set_player_type(duer_audio_type_t num)
{
    duer_playing_type = num;
    return 0;
}


int chat_audio_sync_play_tone(const char *uri)
{
    esp_audio_sync_play(player, uri, 0);
    return 0;
}
void chat_audio_play_tone(const char *url)
{
    esp_audio_play(player, AUDIO_CODEC_TYPE_DECODER, url, 0);
}

void chat_audio_active_paused()
{

    ESP_LOGD(TAG, "duer_dcs_audio_active_paused");
}

void chat_audio_active_play()
{

    ESP_LOGD(TAG, "duer_dcs_audio_active_play");
}

void chat_vad_stop() {
    audio_recorder_vad_check_enable(recorder,false);
}
void chat_vad_start() {
    audio_recorder_vad_check_enable(recorder,true);
}

void chat_wwe_stop() {
    audio_recorder_wakenet_enable(recorder,false);
}
void chat_wwe_start() {
    audio_recorder_wakenet_enable(recorder,true);
}

bool chat_is_wakeuping() {
    if(audio_recorder_get_wakeup_state(recorder)) {
        return true;
    }else {
        return false;
    }
}
void chat_shutup() {
    esp_audio_stop(player, TERMINATION_TYPE_NOW);
    chat_playing_type = PLAY_STOP;
    is_send_done = false;
    clear_url_list();
}
